from inspect import getgeneratorstate


# 协程
# 1、生成器如何进化成协程
# 2、用作协程的生成器的基本行为
def simple_coroutine():
    print('-> coroutine started')
    x = yield
    print('-> coroutine received:', x)


my_coro = simple_coroutine()
# TypeError: can't send non-None value to a just-started generator
# my_coro.send(1234)
print(my_coro)
# 要调用 next(...) 函数，因为生成器还没启动，没在 yield 语句处暂停，所以一开始无法发送数据
next(my_coro)
try:
    my_coro.send(42)  # 执行完成 StopIteration
except StopIteration:
    pass


# 协程状态 inspect.getgeneratorstate()
# 'GEN_CREATED' 等待开始执行
# 'GEN_RUNNING' 解释器正在执行
# 'GEN_SUSPENDED' 在yield表达式处暂停
# 'GEN_CLOSED' 执行结束
def simple_coro2(a):
    print('-> Started:a=', a)
    b = yield a
    print('-> Received:b=', b)
    c = yield a + b
    print('-> Received:c=', c)


my_coro2 = simple_coro2(14)
print(getgeneratorstate(my_coro2))
print(next(my_coro2))
print(getgeneratorstate(my_coro2))
print(my_coro2.send(28))
try:
    my_coro2.send(99)
except StopIteration:
    pass
print(getgeneratorstate(my_coro2))

# 3、示例：使用协程计算移动平均值
# @see coroaverager0.py
# 4、预激协程的装饰器
# @see coroaverager1.py
# 5、终止协程和异常处理
# 未处理的异常会导致协程终止
from coroaverager1 import averager

coro_avg = averager()
print(coro_avg.send(40))
print(coro_avg.send(50))


# print(coro_avg.send('spam'))
# print(coro_avg.send(60))

# generator.throw(exc_type[, exc_value[, traceback]])致使生成器在暂停的 yield 表达式处抛出指定的异常。如果生成
# 器处理了抛出的异常，代码会向前执行到下一个 yield 表达式，而产出的值会成为调用 generator.throw 方法得到的返回值。如果生成器
# 没有处理抛出的异常，异常会向上冒泡，传到调用方的上下文中。
# generator.close()
# 致使生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。如果生成器没有处理这个异常，或者抛出了 StopIteration 异常（通
# 常是指运行到结尾），调用方不会报错。如果收到 GeneratorExit 异常，生成器一定不能产出值，否则解释器会抛出 RuntimeError 异常。
# 生成器抛出的其他异常会向上冒泡，传给调用方。
# @see coro_exc_demo.py coro_finally_demo.py

# 6、让协程返回值
# @see coroaverager2.py

# 7、使用yield from
def gen():
    yield from 'AB'
    yield from range(1, 3)


print(list(gen()))
# yield from 的主要功能是打开双向通道，把最外层的调用方与最内层的子生成器连接起来，这样二者可以直接发送和产出值
# 还可以直接传入异常，而不用在位于中间的协程中添加大量处理异常的样板代码
# @ see coroaverager3.py

# 8、yield from的意义
# ...
# 9、使用案例：使用协程做离散事件仿真
# @ taxi_sim.py
